package com.lizhi.websocket.server;/**
 * @program: lizhi-vue
 * @name WebSocketServer
 * @description: websocket消息通知
 */

import com.lizhi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *@author:
 *@program: lizhi-vue
 *@name WebSocketServer
 *@description: websocket消息通知
 */
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/notice/{userId}")
public class WebSocketServer {

    /**静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;

    /**concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。pc-WebSocketServer*/
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    /** 在线用户 pc-Session*/
    private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**连接用户信息*/
    private static ConcurrentHashMap<String, Set<String>> userMap = new ConcurrentHashMap<>();

    /**
     * 有连接请求进来时
     * @param userId
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam(value = "userId") String userId, Session session){
        String sessionId = session.getId();
        String socketId = sessionId + "-" + userId;
        if (userMap.containsKey(userId)){
            Set<String> socketIds = userMap.get(userId);
            socketIds.add(socketId);
            userMap.put(userId, socketIds);
        } else {
            Set<String> socketIds = new HashSet<>();
            socketIds.add(socketId);
            userMap.put(userId, socketIds);
        }
        if (sessionMap.containsKey(socketId)){
            sessionMap.remove(socketId);
            sessionMap.put(socketId, session);
        } else {
            sessionMap.put(socketId, session);
        }

        log.info("打开pcWebSocket连接");
        if (webSocketMap.containsKey(socketId)){
            webSocketMap.remove(socketId);
            webSocketMap.put(socketId, this);
        } else {
            webSocketMap.put(socketId, this);
            //在线数加1
            addOnlineCount();
        }
        //当前连接的用户
        log.info("用户{}连接,当前在线人数为:{}", socketId, getOnlineCount());

        //连接成功之后判断是否有未消费的消息
    }

    /**
     *功能描述 收到消息时执行
     * @author lizhi
     * @date 2022/5/21
     * @param userId: 消息发送人
     * @param message: 收到的消息
     * @return void
     */
    @OnMessage
    public void onMessage(@PathParam(value = "userId") String userId, String message, Session session) {
        String sessionId = session.getId();
        String socketId = sessionId + "-" + userId;
        log.info("收到{}发送的消息:{}", socketId, message);
        WebSocketServer.sendSelfMessage(userId, message);
    }

    /**
     *功能描述 断开连接时执行
     * @author lizhi
     * @date 2022/5/21
     * @param
     * @return void
     */
    @OnClose
    public void onClose(@PathParam(value = "userId") String userId, Session session){
        String sessionId = session.getId();
        String socketId = sessionId + "-" + userId;
        if (userMap.containsKey(userId)){
            Set<String> socketIds = userMap.get(userId);
            socketIds.remove(socketId);
            userMap.put(userId, socketIds);
        }
        if(webSocketMap.containsKey(socketId)){
            webSocketMap.remove(socketId);
            //从set中删除
            subOnlineCount();
        }

        if (sessionMap.containsKey(socketId)){
            sessionMap.remove(socketId);
        }

        log.info("用户退出,当前在线人数为:" + getOnlineCount());
    }

    /**
     *功能描述 连接出错时执行
     * @author lizhi
     * @date 2022/5/21
     * @param userId:
     * @param error:
     * @return void
     */
    @OnError
    public void onError(@PathParam(value = "userId") String userId, Session session, Throwable error){
        String sessionId = session.getId();
        String socketId = sessionId + "-" + userId;
        log.error("用户{}错误,原因-Exception:{}", socketId, error.getMessage());
        if (userMap.containsKey(userId)){
            Set<String> socketIds = userMap.get(userId);
            socketIds.remove(socketId);
            userMap.put(userId, socketIds);
        }

        if(webSocketMap.containsKey(socketId)){
            webSocketMap.remove(socketId);
            //从set中删除
            subOnlineCount();
        }
        if (sessionMap.containsKey(socketId)){
            sessionMap.remove(socketId);
        }
    }

    /**
     *功能描述 发送消息
     * @author lizhi
     * @date 2022/5/21
     * @return void
     */
    public void sendMessage(Session session, String message) throws IOException {
        if (session != null){
            synchronized (this){
                session.getBasicRemote().sendText(message);
            }
        } else {
            log.error("发送session不能为空");
        }
    }

    /**
     * 功能描述 给指定用户发送消息
     *
     * @param message: 要发送的消息
     * @param userId   用户id
     * @author lizhi
     * @date 2022/5/21
     */
    public static void sendSelfMessage(String userId, String message){
        try {
            Set<String> socketIds = userMap.get(userId);
            if (!StringUtils.isNull(socketIds)){
                for (String socketId : socketIds){
                    if (webSocketMap.containsKey(socketId) && sessionMap.containsKey(socketId)){
                        webSocketMap.get(socketId).sendMessage(sessionMap.get(socketId), message);
                    } else {
                        log.error("用户{},不在线！", socketId);
                    }
                }
            } else {
                log.error("暂无用户在线！");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *功能描述 获取当前已连接数
     * @author lizhi
     * @date 2022/5/21
     * @param
     * @return int
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     *功能描述 当前连接数加一
     * @author lizhi
     * @date 2022/5/21
     * @param
     * @return void
     */
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    /**
     *功能描述 当前连接数减一
     * @author lizhi
     * @date 2022/5/21
     * @param
     * @return void
     */
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    /**
     *功能描述 当前在线人
     * @author lizhi
     * @date 2022/5/21
     * @param
     * @return java.util.concurrent.ConcurrentHashMap<java.lang.String,javax.websocket.Session>
     */
    public static ConcurrentHashMap<String, Session> getSessionMap(){
        return sessionMap;
    }

}
